package com.hxgis.statisticanalysis.intelligentIndicator.service;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hxgis.export.DocxResolver;
import com.hxgis.export.ExpressionResolver;
import com.hxgis.statisticanalysis.config.UyunConfig;
import com.hxgis.statisticanalysis.intelligentIndicator.mapper.WorkOrderInfoMapper;
import com.hxgis.statisticanalysis.intelligentIndicator.pojo.TicketModelType;
import com.hxgis.statisticanalysis.intelligentIndicator.pojo.WorkOrderInfo;
import com.hxgis.statisticanalysis.intelligentIndicator.vo.CompletedWorkOrderStatistic;
import com.hxgis.statisticanalysis.intelligentIndicator.vo.WorkOrderQuery;
import com.hxgis.statisticanalysis.intelligentIndicator.vo.WorkOrderStatistic;
import com.hxgis.statisticanalysis.intelligentIndicator.vo.WorkOrderWordBean;
import com.hxgis.statisticanalysis.utils.ExportUtil;
import com.hxgis.statisticanalysis.utils.TableMergeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.xmlbeans.XmlException;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 赵恒
 * @date 2022/12/12
 */
@Service
@Slf4j
public class WorkOrderInfoService extends ServiceImpl<WorkOrderInfoMapper, WorkOrderInfo> {
    @Resource
    WorkOrderInfoMapper workOrderInfoMapper;

    @Resource
    RestTemplate restTemplate;


    @Resource
    TicketModelTypeService ticketModelTypeService;

    @Resource
    UyunConfig uyunConfig;

    @Resource
    WorkOrderTriggerEventService triggerEventService;

    ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 用于对工单模型进行排序，是导出的两张表不至于顺序错乱
     */
    private Map<String, Integer> wordOrderSortMap = new HashMap<>();

    /**
     * 查询工单
     *
     * @date 2022/11/09 15:20
     */
    public final static String TICKETS_QUERY = "/itsm/openapi/v3/tickets/query";

    /**
     * 更新所有的工单
     */
    public void updateAll() {
        log.info("采集工单的任务开始执行");
        HashMap hashMap = new HashMap();
        hashMap.put("order_by", "createTime");
        hashMap.put("page_size", 50000);

        JSONObject result = restTemplate.postForObject(uyunConfig.getIp() + TICKETS_QUERY + "?apikey=" + uyunConfig.getKey(), hashMap, JSONObject.class);
        Object o = result.get("ticket_list");
        List<WorkOrderInfo> workOrderInfoList = null;
        try {
            workOrderInfoList = objectMapper.readValue(objectMapper.writeValueAsString(o), new TypeReference<List<WorkOrderInfo>>() {
            });
            log.info("工单获取成功，数量:{}", workOrderInfoList.size());
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.saveOrUpdateBatch(workOrderInfoList);
        log.info("工单更新到数据库成功");
    }

    /**
     * 根据条件查询工单
     */
    public List<WorkOrderInfo> query(WorkOrderQuery workOrderQuery) {
        int pageNo = workOrderQuery.getPageNo();
        int pageSize = workOrderQuery.getPageSize();
        QueryWrapper<WorkOrderInfo> queryWrapper = new QueryWrapper<>();
        if (workOrderQuery.getStart() != null) {
            queryWrapper.gt("create_time", LocalDateTimeUtil.of(workOrderQuery.getStart()));
        }
        if (workOrderQuery.getEnd() != null) {
            queryWrapper.lt("create_time", LocalDateTimeUtil.of(workOrderQuery.getEnd()));
        }
        Page<WorkOrderInfo> page = new Page<>(pageNo, pageSize);
        workOrderInfoMapper.selectPage(page, queryWrapper);
        log.info("工单数量：{}", page.getSize());

        return page.getRecords();
    }

    /**
     * 按月份更新工单
     */
    public boolean updateByMonth(String startMonth, String endMonth) {
        DateTime startDateTime = DateUtil.parse(startMonth, "yyyy-MM");
        // 结束时间需要使用月末时间
        DateTime endDateTime = DateUtil.endOfMonth(DateUtil.parse(endMonth, "yyyy-MM"));
        log.info("start:{}, end:{}", startDateTime, endDateTime);

        log.info("采集工单的任务开始执行");
        Map<String, Object> hashMap = new HashMap<>();
        hashMap.put("order_by", "createTime");
        hashMap.put("page_size", 50000);
        hashMap.put("start_time", startDateTime.getTime() / 1000);
        hashMap.put("end_time", endDateTime.getTime() / 1000);

        JSONObject result = restTemplate.postForObject(uyunConfig.getIp() + TICKETS_QUERY + "?apikey=" + uyunConfig.getKey(), hashMap, JSONObject.class);
        Object o = result.get("ticket_list");
        List<WorkOrderInfo> workOrderInfoList = null;
        try {
            workOrderInfoList = objectMapper.readValue(objectMapper.writeValueAsString(o), new TypeReference<List<WorkOrderInfo>>() {
            });
            log.info("工单获取成功，数量:{}", workOrderInfoList.size());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return this.saveOrUpdateBatch(workOrderInfoList);
    }

    /**
     * 根据月份范围查询
     *
     * @param startMonth
     * @param endMonth
     * @return
     */
    public List<WorkOrderInfo> getWorkOrderInfoByMonth(String startMonth, String endMonth) {
        DateTime startDateTime = DateUtil.parse(startMonth, "yyyy-MM");
        // 结束时间需要使用月末时间
        DateTime endDateTime = DateUtil.endOfMonth(DateUtil.parse(endMonth, "yyyy-MM"));
        log.info("start:{}, end:{}", startDateTime, endDateTime);

        // 从数据库获取时间范围内的工单集合
        QueryWrapper<WorkOrderInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("create_time", startDateTime);
        queryWrapper.lt("create_time", endDateTime);
        return this.list(queryWrapper);
    }

    /**
     * 导出word文档
     */
    public void exportWord(String startMonth, String endMonth, HttpServletResponse response) {
        // 将时间转化为汉字格式
        String startMonthStr = DateUtil.format(DateUtil.parse(startMonth, "yyyy-MM"), "yyyy年MM月");
        String endMonthStr = DateUtil.format(DateUtil.endOfMonth(DateUtil.parse(endMonth, "yyyy-MM")), "yyyy年MM月");

        List<WorkOrderInfo> workOrderInfoList = getWorkOrderInfoByMonth(startMonth, endMonth);
        // 将工单集合转化为要导出的工单统计集合
        List<WorkOrderStatistic> workOrderStatisticList = getWorkOrderStatistic(workOrderInfoList);
        log.info("气象服务流程工单统计表：{}", JSONUtil.toJsonStr(workOrderStatisticList));

        // 将工单集合转化为要导出的已解决工单统计集合
        List<CompletedWorkOrderStatistic> completedWorkOrderStatisticList = getCompletedWorkOrderStatistic(workOrderInfoList);
        log.info("气象服务流程已解决工单统计表：{}", JSONUtil.toJsonStr(completedWorkOrderStatisticList));


        // 构造Word文档的模板对象
        WorkOrderWordBean workOrderWordBean = new WorkOrderWordBean();
        workOrderWordBean.setStartTime(startMonthStr);
        workOrderWordBean.setEndTime(endMonthStr);
        workOrderWordBean.setTableList1(workOrderStatisticList);
        workOrderWordBean.setTableList2(completedWorkOrderStatisticList);


        // 读取word文档模板
        InputStream fileStream;
        try {
            fileStream = getResourceTemplatePath("data/workOrderTemplate.docx");
            if (fileStream == null) {
                throw new FileNotFoundException("模板文件" + "不存在！");
            }
            DocxResolver docxResolver = new DocxResolver(new ExpressionResolver(workOrderWordBean));
            XWPFDocument document = new XWPFDocument(fileStream);
            docxResolver.resolve(document);
            // 操作第一张表
            List<XWPFTable> xwpfTableList = document.getTables();
            XWPFTable xwpfTable = xwpfTableList.get(0);
            // 找到几个需要合并的行
            if (workOrderStatisticList.size() > 1) {
                int startRow = 1;
                int endRow = 1;
                for (int i = 1; i < workOrderStatisticList.size(); i++) {
                    // 比较当前行和上一行，如果一致则接着往下，否则合并之前的
                    WorkOrderStatistic workOrderStatistic = workOrderStatisticList.get(i);
                    WorkOrderStatistic workOrderStatistic1 = workOrderStatisticList.get(i - 1);
                    endRow += 1;
                    // 如果到了集合的结尾，分两种情况，1.最后一行与上一行一致则直接合并，并结束；2.不一致
                    if (i == workOrderStatisticList.size() - 1) {
                        if (workOrderStatistic.getServiceType().equals(workOrderStatistic1.getServiceType())) {
                            TableMergeUtil.mergeCellsVertically(xwpfTable, 1, startRow, endRow);
                            TableMergeUtil.mergeCellsVertically(xwpfTable, 2, startRow, endRow);
                        } else {
                            TableMergeUtil.mergeCellsVertically(xwpfTable, 1, startRow, endRow - 1);
                            TableMergeUtil.mergeCellsVertically(xwpfTable, 2, startRow, endRow - 1);
                            // 最后一行需要合并前面的两列
                            TableMergeUtil.mergeCellsHorizontal(xwpfTable, i + 1, 0, 1);
                        }
                        break;
                    }
                    if (!workOrderStatistic.getServiceType().equals(workOrderStatistic1.getServiceType())) {
                        // 合并前面的单元格
                        TableMergeUtil.mergeCellsVertically(xwpfTable, 1, startRow, endRow);
                        TableMergeUtil.mergeCellsVertically(xwpfTable, 2, startRow, endRow);
                        // 重新赋值
                        startRow = endRow;
                    }
                }
            }

            // 浏览器导出文件
            String fileName = endMonthStr + "广西气象宽带网及网络设备故障情况报告";
            ExportUtil.exportDoc(response, document, fileName);
            document.close();
            fileStream.close();
        } catch (XmlException | IOException | InvalidFormatException e) {
            e.printStackTrace();
        }
    }

    public InputStream getResourceTemplatePath(String path) throws IOException {
        ClassPathResource classPathResource = new ClassPathResource(path);
        return classPathResource.getInputStream();
    }

    public List<WorkOrderStatistic> getWorkOrderStatistic(List<WorkOrderInfo> workOrderInfoList) {
        List<WorkOrderStatistic> workOrderStatisticList = new ArrayList<>();
        // 将工单模型名称作为key, 获取Map
        Map<String, List<WorkOrderInfo>> workOrderInfoMap = workOrderInfoList.stream().collect(Collectors.groupingBy(WorkOrderInfo::getModelName));
        List<TicketModelType> ticketModelTypeList = ticketModelTypeService.list();
        Map<String, TicketModelType> ticketModelTypeMap = ticketModelTypeList.stream().collect(Collectors.toMap(TicketModelType::getTicketModelName, v1 -> v1, (v1, v2) -> v1));
        for (String key : workOrderInfoMap.keySet()) {
            List<WorkOrderInfo> workOrderInfos = workOrderInfoMap.get(key);
            String serviceType = "";
            if (ticketModelTypeMap.get(key) == null) {
                log.error("{}的serviceType不存在", key);
                continue;
            } else {
                serviceType = ticketModelTypeMap.get(key).getServiceType();
            }

            WorkOrderStatistic workOrderStatistic = new WorkOrderStatistic();
            workOrderStatistic.setWorkOrderModelName(key);
            workOrderStatistic.setServiceType(serviceType);

            // 计算完成的工单
            long completedCount = workOrderInfos.stream().filter(workOrderInfo -> workOrderInfo.getStatus() == 3 || workOrderInfo.getStatus() == 7).count();
            // 计算未完成的工单
            long uncompletedCount = workOrderInfos.stream().filter(workOrderInfo -> workOrderInfo.getStatus() == 1 || workOrderInfo.getStatus() == 2).count();

            workOrderStatistic.setCompletedWorkOrderTotal(String.valueOf(completedCount));
            workOrderStatistic.setUncompletedWorkOrderTotal(String.valueOf(uncompletedCount));
            workOrderStatistic.setWorkOrderTotal(String.valueOf(completedCount + uncompletedCount));
            // 分母不能为0
            if ((completedCount + uncompletedCount) == 0) {
                workOrderStatistic.setCompletedRate("0%");
            } else {
                workOrderStatistic.setCompletedRate(NumberUtil.decimalFormat("#.##%", completedCount * 1.0 / (completedCount + uncompletedCount)));
            }
            workOrderStatisticList.add(workOrderStatistic);
        }


        // 对于工单中没有的工单模型，需要补充上去
        for (String key : ticketModelTypeMap.keySet()) {
            if (workOrderInfoMap.containsKey(key)) {
                continue;
            }
            WorkOrderStatistic workOrderStatistic = new WorkOrderStatistic();
            workOrderStatistic.setWorkOrderModelName(key);
            workOrderStatistic.setServiceType(ticketModelTypeMap.get(key).getServiceType());
            workOrderStatistic.setWorkOrderTotal("0");
            workOrderStatistic.setCompletedWorkOrderTotal("0");
            workOrderStatistic.setUncompletedWorkOrderTotal("0");
            workOrderStatistic.setCompletedRate("0%");
            workOrderStatisticList.add(workOrderStatistic);
        }

        // 对于属于同一模型类型的，设置相同的工单总数值
        Map<String, List<WorkOrderStatistic>> workOrderStatisticMap = workOrderStatisticList.stream().collect(Collectors.groupingBy(WorkOrderStatistic::getServiceType));
        for (WorkOrderStatistic workOrderStatistic : workOrderStatisticList) {
            String serviceType = workOrderStatistic.getServiceType();
            List<WorkOrderStatistic> orderStatisticList = workOrderStatisticMap.get(serviceType);
            int size = 0;
            for (WorkOrderStatistic orderStatistic : orderStatisticList) {
                size += Integer.parseInt(orderStatistic.getWorkOrderTotal());
            }
            workOrderStatistic.setTypeStatisticNum(String.valueOf(size));
        }

        // 按照模型服务类型排序
        workOrderStatisticList = workOrderStatisticList.stream()
                .sorted(Comparator.comparing(WorkOrderStatistic::getServiceType))
                .collect(Collectors.toList());

        // 更新工单排序
        for (int i = 0; i < workOrderStatisticList.size(); i++) {
            wordOrderSortMap.put(workOrderStatisticList.get(i).getWorkOrderModelName(), i);
        }

        // 在最后添加汇总对象
        WorkOrderStatistic totalWorkOrderStatistic = generateTotalStaticObject(workOrderStatisticList);
        workOrderStatisticList.add(totalWorkOrderStatistic);


        return workOrderStatisticList;
    }

    /**
     * 将工单信息列表，转化为已完成工单导出word对应的table的集合
     *
     * @param workOrderInfoList
     * @return
     */
    public List<CompletedWorkOrderStatistic> getCompletedWorkOrderStatistic(List<WorkOrderInfo> workOrderInfoList) {
        List<CompletedWorkOrderStatistic> completedWorkOrderStatisticList = new ArrayList<>();
        // 先获取工单处理时长，将工单模型名称作为key, 获取Map
        workOrderInfoList.forEach(workOrderInfo -> {
            // 工单ID
            String orderInfoId = workOrderInfo.getId();
            // 从数据库查询该工单的结束时间
            Date completedTime = triggerEventService.getCompletedTime(orderInfoId);
            Date createTime = workOrderInfo.getCreateTime();
            // 计算时差
            double hours = 0;
            if (createTime != null && completedTime != null) {
                long between = DateUtil.between(createTime, completedTime, DateUnit.MS);
                // 转换为小时
                hours = between * 1.0 / (1000 * 60 * 60);
            }
            workOrderInfo.setCompleteHour(hours);
        });
        log.info("workOrderInfoList:{}", JSONUtil.toJsonStr(workOrderInfoList));
        Map<String, List<WorkOrderInfo>> workOrderInfoMap = workOrderInfoList.stream()
                .collect(Collectors.groupingBy(WorkOrderInfo::getModelName));
        log.info("workOrderInfoMap:{}", JSONUtil.toJsonStr(workOrderInfoMap));

        // 排除掉不需要的工单模型
        List<TicketModelType> ticketModelTypeList = ticketModelTypeService.list();
        Map<String, TicketModelType> ticketModelTypeMap = ticketModelTypeList.stream().collect(Collectors.toMap(TicketModelType::getTicketModelName, v1 -> v1, (v1, v2) -> v1));

        for (String key : workOrderInfoMap.keySet()) {
            if (!ticketModelTypeMap.containsKey(key)) {
                continue;
            }
            List<WorkOrderInfo> workOrderInfos = workOrderInfoMap.get(key);
            // 如果当前的工单模型下没有工单，则直接赋值0
            List<WorkOrderInfo> completedWorkOrderInfoList = workOrderInfos.stream()
                    .filter(workOrderInfo -> workOrderInfo.getStatus() == 3 || workOrderInfo.getStatus() == 7)
                    .collect(Collectors.toList());
            // 获取已完成的工单
            log.info("key:{}, completedWorkOrderInfoList:{}", key, JSONUtil.toJsonStr(completedWorkOrderInfoList));
            if (CollectionUtils.isEmpty(completedWorkOrderInfoList)) {
                CompletedWorkOrderStatistic completedWorkOrderStatistic = new CompletedWorkOrderStatistic();
                completedWorkOrderStatistic.setWorkOrderModelName(key);
                completedWorkOrderStatistic.setCompletedWorkOrderNum("0");
                completedWorkOrderStatistic.setTotalCompletedHours("0");
                completedWorkOrderStatistic.setLongestHandleHours("0");
                completedWorkOrderStatistic.setShortestHandleHours("0");
                completedWorkOrderStatistic.setAverageHandleHours("0");
                completedWorkOrderStatisticList.add(completedWorkOrderStatistic);
                continue;
            }

            CompletedWorkOrderStatistic completedWorkOrderStatistic = new CompletedWorkOrderStatistic();
            // 计算当前模型中工单的各个参数
            completedWorkOrderStatistic.setWorkOrderModelName(key);

            // 工单完成数
            completedWorkOrderStatistic.setCompletedWorkOrderNum(String.valueOf(completedWorkOrderInfoList.size()));

            // 总完成时间
            Double totalCompletedHours = completedWorkOrderInfoList.stream().mapToDouble(WorkOrderInfo::getCompleteHour).sum();
            completedWorkOrderStatistic.setTotalCompletedHours(NumberUtil.decimalFormat("#.#", totalCompletedHours));

            // 平均处理时间
            completedWorkOrderStatistic.setAverageHandleHours(NumberUtil.decimalFormat("#.#", totalCompletedHours / completedWorkOrderInfoList.size()));

            // 最长处理时间
            WorkOrderInfo max = completedWorkOrderInfoList.stream().max(Comparator.comparingDouble(WorkOrderInfo::getCompleteHour)).get();
            completedWorkOrderStatistic.setLongestHandleHours(NumberUtil.decimalFormat("#.#", max.getCompleteHour()));

            // 最短处理时间
            WorkOrderInfo min = completedWorkOrderInfoList.stream().min(Comparator.comparingDouble(WorkOrderInfo::getCompleteHour)).get();
            completedWorkOrderStatistic.setShortestHandleHours(NumberUtil.decimalFormat("#.#", min.getCompleteHour()));

            completedWorkOrderStatisticList.add(completedWorkOrderStatistic);
        }

        // 对于工单中没有的工单模型，需要补充上去
        for (String key : ticketModelTypeMap.keySet()) {
            if (workOrderInfoMap.containsKey(key)) {
                continue;
            }
            CompletedWorkOrderStatistic completedWorkOrderStatistic = new CompletedWorkOrderStatistic();
            completedWorkOrderStatistic.setWorkOrderModelName(key);
            completedWorkOrderStatistic.setCompletedWorkOrderNum("0");
            completedWorkOrderStatistic.setTotalCompletedHours("0");
            completedWorkOrderStatistic.setLongestHandleHours("0");
            completedWorkOrderStatistic.setShortestHandleHours("0");
            completedWorkOrderStatistic.setAverageHandleHours("0");
            completedWorkOrderStatisticList.add(completedWorkOrderStatistic);
        }

        // 重新排序
        completedWorkOrderStatisticList = completedWorkOrderStatisticList.stream().sorted(new Comparator<CompletedWorkOrderStatistic>() {
            @Override
            public int compare(CompletedWorkOrderStatistic o1, CompletedWorkOrderStatistic o2) {
                Integer i1 = wordOrderSortMap.getOrDefault(o1.getWorkOrderModelName(), 99);
                Integer i2 = wordOrderSortMap.getOrDefault(o2.getWorkOrderModelName(), 99);
                return i1.compareTo(i2);
            }
        }).collect(Collectors.toList());

        return completedWorkOrderStatisticList;
    }

    /**
     * 生成汇总对象
     *
     * @param workOrderStatisticList
     * @return
     */
    public WorkOrderStatistic generateTotalStaticObject(List<WorkOrderStatistic> workOrderStatisticList) {
        WorkOrderStatistic totalWorkOrderStatistic = new WorkOrderStatistic();
        totalWorkOrderStatistic.setWorkOrderModelName("汇总总数");
        totalWorkOrderStatistic.setServiceType("汇总总数");
        // 计算工单总数
        int orderTotal = 0;
        int completedOrderTotal = 0;
        int uncompletedOrderTotal = 0;
        for (WorkOrderStatistic orderStatistic : workOrderStatisticList) {
            orderTotal += Integer.parseInt(orderStatistic.getWorkOrderTotal());
            completedOrderTotal += Integer.parseInt(orderStatistic.getCompletedWorkOrderTotal());
            uncompletedOrderTotal += Integer.parseInt(orderStatistic.getUncompletedWorkOrderTotal());
        }
        totalWorkOrderStatistic.setWorkOrderTotal(String.valueOf(orderTotal));
        totalWorkOrderStatistic.setTypeStatisticNum(totalWorkOrderStatistic.getWorkOrderTotal());
        totalWorkOrderStatistic.setCompletedWorkOrderTotal(String.valueOf(completedOrderTotal));
        totalWorkOrderStatistic.setUncompletedWorkOrderTotal(String.valueOf(uncompletedOrderTotal));
        if (orderTotal == 0) {
            totalWorkOrderStatistic.setCompletedRate("0");
        } else {
            totalWorkOrderStatistic.setCompletedRate(NumberUtil.decimalFormat("#.##%", completedOrderTotal * 1.0 / orderTotal));
        }
        return totalWorkOrderStatistic;
    }
}
